package com.alphawavesgames.engine.rendering;

import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 *
 * @author Duncan
 * Manages the rendering of a frame using multiple threads.
 * A game thread can use beginScene()/endScene() to surround
 * any code that builds up a rendering commands queue for a frame.
 * A render thread uses the internal startRender()/endRender()
 * to surround code that uses the render command queue to render the frame.
 * A resource updating thread uses beginResourceUpdate()/endResourceUpdate()
 * to surround code where it updates rendering resources that might be used
 * in the render thread to render a frame.
 * The game and resource update threads are blocked while the render thread is
 * working but can run concurrently with each other.
 *
 * Todo : think about how to stop the render thread from processing a command queue
 * more than once before another call to beginScene. i.e. for every beginScene() there
 * should be exactly one matching startRender().
 */
public class SceneRenderer
{
    protected boolean   m_inScene;
    protected int       m_currentSceneFrame;
    protected int       m_lastRenderedFrame;
    protected final ReentrantLock m_sceneLock = new ReentrantLock();

    //protected final ReentrantReadWriteLock.ReadLock m_readSceneLock = m_sceneLock.readLock();
    //protected final ReentrantReadWriteLock.WriteLock m_writeSceneLock = m_sceneLock.writeLock();

    public SceneRenderer()
    {
        m_inScene = false;
        m_currentSceneFrame = 0;
        m_lastRenderedFrame = 0;
    }

    //use read lock to indicate render resources are being used on a thread.
    /*public void beginResourceUpdate()
    {
        //wait until rendering of frame is finished
        m_readSceneLock.lock();
    }

    public void endResourceUpdate()
    {
        //release the lock
        m_readSceneLock.unlock();
    }*/

    //begin a scene ( single frame of rendering )
    public void beginScene()
    {
        //assert already in a scene
        assert( m_inScene == false );

        //wait until rendering of last scene has finished
        m_sceneLock.lock();

        //mark as in scene
        m_inScene = true;
        //inc scene frame count
        //unlikely but might as well...
        if( m_currentSceneFrame == Integer.MAX_VALUE )
        {
            m_currentSceneFrame = 0;
            m_lastRenderedFrame = 0;
        }
        m_currentSceneFrame++;
    }

    //end the scene
    public void endScene()
    {
        //assert already in a scene
        assert( m_inScene == true );

        //mark as out of scene
        m_inScene = false;

        //release the lock
        m_sceneLock.unlock();
    }

    //indicate the start of rendering. this blocks building a scene
    //and also updating resources by using the write lock
    protected void startRender()
    {
        //assert already in a scene
        assert( m_inScene == false );

        m_sceneLock.lock();
    }

    protected void endRender()
    {
        //assert already in a scene
        assert( m_inScene == false );

        m_lastRenderedFrame = m_currentSceneFrame;
        m_sceneLock.unlock();
    }
}
